#include "StdAfx.h"
#include "GalconCommonTypes.h"
#include "Message.h"
#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <string>
#include <sstream>
#include "GameState.h"
#include "MessageBuilder.h"
#include "FlightInfo.h"
#include "ShipsArrivedInfo.h"

namespace MessageBuilder
{
   template<typename MessageBuilderFunction>
   MessagePtr GetMessage(MessageBuilderFunction messageBuilderFunction)
   {
      MessagePtr msgPtr = boost::make_shared<Message>();

      std::string message;

      message.assign(messageBuilderFunction());

      if (message.length() > Message::max_body_length)
         throw std::exception("Message length is more than max_body_length");

      msgPtr->SetBodyLength(message.length());
      msgPtr->SetBody(message.c_str());
      msgPtr->EncodeHeader();
      return msgPtr;
   }

   std::string GetGameStateMessage( GameStatePtr gameState)
   {
      std::stringstream ss;
      ss << FromServerToClients << " " << PingMessage << " ";
      
      ss << "Planets";
      const GameState::PlanetsMap planets = gameState->GetPlanets();
      
      BOOST_FOREACH(const GameState::PlanetsMapPair& planet, planets)
      {
         ss << " " << planet.first << " " << planet.second->GetPlayerID() << " " << planet.second->GetCountOfShips();
      }

      ss << " Players";
      const GameState::PlayersMap& playersMap = gameState->GetPlayers();
      
      BOOST_FOREACH(const GameState::PlayerMapPair& player, playersMap)
      {
         ss << " " << player.first << " " << player.second->PlayerColor();
      }

      return ss.str();
   }

   std::string GetGameStateInitialMessage( GameStatePtr gameState, PlayerID playerID, GameColors playerColor)
   {
      std::stringstream ss;
      ss << FromServerToClients << " " << InitialMessage << " ";
      ss << "Player " << playerID << " " << playerColor << " ";
      

      GameState::PlanetsMap planets = gameState->GetPlanets();
      GameState::PlayersMap players = gameState->GetPlayers();
      ss << "Planets";
      BOOST_FOREACH(GameState::PlanetsMapPair& planet, planets)
      {
         ss << " " << planet.first << " " << planet.second->GetX() << " "
            << planet.second->GetY() << " " << planet.second->GetPlayerID() << " "
            << planet.second->GetCountOfShips() << " " << planet.second->GetPlanetSize()
            << " " << planet.second->GetPlanetTextureID();
      }

      ss << " Players";

      BOOST_FOREACH(GameState::PlayerMapPair& player, players)
      {
         ss << " " << player.first << " " << player.second->PlayerColor();
      }

      return ss.str();
   }

   std::string CreateRejectionMessage( RejectionReason rejectionReason)
   {
      std::stringstream ss;
      ss << FromServerToClient << " " << TooManyPlayers << " ";
      ss << rejectionReasonDscr[rejectionReason];
      
      return ss.str();
   }

   std::string CreateLaunchShipsMessage(const FlightInfoPtr flightInfoPtr)
   {
      std::stringstream ss;
      ss << FromClientToAll << " " << ShipsLaunched << " ";

      const PlanetIDMap& planetIDs = flightInfoPtr->GetSrcPlanets();
      ss << "SrcPlanets ";

      BOOST_FOREACH(const PlanetShipsCountPair& currentPlanetShipsCountPair, planetIDs)
      {
         ss << currentPlanetShipsCountPair.first << " " << currentPlanetShipsCountPair.second << " ";
      }

      ss << "DstPlanet " << flightInfoPtr->GetDstPlanetID();

      return ss.str();
   }

   std::string CreateShipsArrivedMessage(const ShipsArrivedInfoPtr shipsArrivedInfoPtr)
   {
      std::stringstream ss;

      ss << FromClientToServer << " " << ShipsArrived;

      ss << " DstPlanet " << shipsArrivedInfoPtr->DstPlanetID()
         << " CountOfShips " << shipsArrivedInfoPtr->CountOfShips()
         << " PlayerOwnerID " << shipsArrivedInfoPtr->GetPlayerID();

      return ss.str();
   }

   std::string CreateErrorDescriptionMessage(const boost::system::error_code& error)
   {
      std::stringstream ss;

      ss << LocalMessage << " " << ErrorMessage;

      ss << " Error occurred: " << error.message();

      return ss.str();
   }

   MessagePtr BuildGameStateMessage(GameStatePtr gameState)
   {
      return GetMessage(boost::bind(GetGameStateMessage, gameState));
   }

   MessagePtr BuildGameStateInitialMessage( GameStatePtr gameState, PlayerID playerID, GameColors playerColor)
   {
      return GetMessage(boost::bind(GetGameStateInitialMessage, gameState, playerID, playerColor));
   }

   MessagePtr BuildRejectionMessage( RejectionReason rejectionReason)
   {
      return GetMessage(boost::bind(CreateRejectionMessage, rejectionReason));
   }

   MessagePtr BuildShipsLaunchedMessage(const FlightInfoPtr flightInfoPtr )
   {
      return GetMessage(boost::bind(CreateLaunchShipsMessage, flightInfoPtr));
   }

   MessagePtr BuildShipsArrivedMessage( const ShipsArrivedInfoPtr shipsArrivedInfoPtr)
   {
      return GetMessage(boost::bind(CreateShipsArrivedMessage, shipsArrivedInfoPtr));
   }

   MessagePtr BuildErrorDescriptionMessage( const boost::system::error_code &error)
   {
      return GetMessage(boost::bind(CreateErrorDescriptionMessage, error));
   }
}
